package ameba.db.model;

import ameba.core.Addon;
import ameba.core.Application;
import ameba.db.DataSourceManager;
import ameba.scanner.ClassFoundEvent;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.Embeddable;
import javax.persistence.Entity;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

/**
 * <p>ModelManager class.</p>
 *
 * @author icode
 * @since 0.1.6e
 */
public class ModelManager extends Addon {

    /**
     * Constant <code>MODULE_MODELS_KEY_PREFIX="db.default.models."</code>
     */
    public static final String MODULE_MODELS_KEY_PREFIX = "db.default.models.";
    private static Logger logger = LoggerFactory.getLogger(ModelManager.class);
    private static Map<String, Set<Class>> modelMap;

    /**
     * <p>getModels.</p>
     *
     * @param name a {@link java.lang.String} object.
     * @return a {@link java.util.Set} object.
     */
    public static Set<Class> getModels(String name) {
        return modelMap.get(name);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setup(final Application application) {
        Map<String, Object> config = application.getSrcProperties();
        modelMap = Maps.newLinkedHashMap();

        Set<String> defaultModelsPkg = Sets.newLinkedHashSet();
        //db.default.models.pkg=
        config.keySet().stream()
                .filter(key -> key.startsWith(MODULE_MODELS_KEY_PREFIX))
                .forEach(key -> {
                    String modelPackages = (String) config.get(key);
                    if (StringUtils.isNotBlank(modelPackages)) {
                        Collections.addAll(defaultModelsPkg, StringUtils.deleteWhitespace(modelPackages).split(","));
                    }
                });

        for (String name : DataSourceManager.getDataSourceNames()) {
            String modelPackages = (String) config.get("db." + name + ".models");
            final Set<String> pkgs = Sets.newHashSet();
            if (StringUtils.isNotBlank(modelPackages)) {
                Collections.addAll(pkgs, StringUtils.deleteWhitespace(modelPackages).split(","));
            }

            if (DataSourceManager.getDefaultDataSourceName().equalsIgnoreCase(name)) {
                pkgs.addAll(defaultModelsPkg);
            }

            //db.default.models.pkg=
            //db.default.models+=
            if (!pkgs.isEmpty()) {
                final String[] startsPackages = pkgs.toArray(new String[pkgs.size()]);
                application.packages(startsPackages);

                final Set<Class> classes = Sets.newHashSet();

                subscribeSystemEvent(ClassFoundEvent.class, event -> event.accept(info -> {
                    if (info.startsWithPackage(startsPackages)) {
                        logger.trace("load class : {}", info.getClassName());
                        Class clazz = info.toClass();
                        if (info.containsAnnotations(Entity.class, Embeddable.class)
                                || Model.class.isAssignableFrom(clazz)) {
                            classes.add(clazz);
                        }
                        return true;
                    }
                    return false;
                }));

                modelMap.put(name, classes);
            }
        }

        defaultModelsPkg.clear();
    }

}
